#pragma once

/*日志器模块
进行模块的整合，向外提供接口完成不同等级日志的输出

    1. 抽象日志器类
    2. 派生出不同的子类：(同步日志器 & 异步日志器)
*/
// #define _GNU_SOURCE
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"

#include <mutex>
#include <atomic>
#include <vector>
#include <cstdarg>
#include <unordered_map>

namespace mylog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name,
               mylog::Level::value level,
               Formatter::ptr formatter,
               std::vector<mylog::LogSink::ptr> sinks)
            : _logger_name(logger_name),
              _limit_level(level),
              _formatter(formatter),
              _sinks(sinks.begin(), sinks.end())
        {}
        //返回日志器名称
        const std::string & getName()
        {
            return _logger_name;
        }
        // 根据不同等级输出日志信息
        // 完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串
        // 交由log接口完成落地输出
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个日志消息对象，进行日志的格式化
            // 1. 比较当前等级和限制等级
            if (mylog::Level::value::DEBUG < _limit_level)
                return;
            // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail !!\n";
                va_end(ap);
                free(res);
                return;
            }
            va_end(ap);
            // 3 4步骤进行封装
            serializa(mylog::Level::value::DEBUG, file, line, res);
            free(res);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个日志消息对象，进行日志的格式化
            // 1. 比较当前等级和限制等级
            if (mylog::Level::value::INFO < _limit_level)
                return;
            // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail !!\n";
                va_end(ap);
                free(res);
                return;
            }
            va_end(ap);
            // 3 4步骤进行封装
            serializa(mylog::Level::value::INFO, file, line, res);
            free(res);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个日志消息对象，进行日志的格式化
            // 1. 比较当前等级和限制等级
            if (mylog::Level::value::WARN < _limit_level)
                return;
            // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail !!\n";
                va_end(ap);
                free(res);
                return;
            }
            va_end(ap);
            // 3 4步骤进行封装
            serializa(mylog::Level::value::WARN, file, line, res);
            free(res);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个日志消息对象，进行日志的格式化
            // 1. 比较当前等级和限制等级
            if (mylog::Level::value::ERROR < _limit_level)
                return;
            // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail !!\n";
                va_end(ap);
                free(res);
                return;
            }
            va_end(ap);
            // 3 4步骤进行封装
            serializa(mylog::Level::value::ERROR, file, line, res);
            free(res);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个日志消息对象，进行日志的格式化
            // 1. 比较当前等级和限制等级
            if (mylog::Level::value::FATAL < _limit_level)
                return;
            // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail !!\n";
                va_end(ap);
                free(res);
                return;
            }
            va_end(ap);
            // 3 4步骤进行封装
            serializa(mylog::Level::value::FATAL, file, line, res);
            free(res);
        }

    protected:
        void serializa(mylog::Level::value level, const std::string &file, size_t line, char *str)
        {
            // 3. 构造LogMsg对象
            LogMsg msg(level, line, file, _logger_name, str);
            // 4. 通过格式化工具对LogMsg进行格式化，得到日志消息字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5. 进行落地输出
            log(ss.str().c_str(), ss.str().size());
        }

        // 抽象接口完成实际的落地输出 不同的日志器会有不同的落地方式
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::string _logger_name;                    // 日志器名称
        std::mutex _mutex;                           // 互斥锁
        std::atomic<mylog::Level::value> _limit_level; // 限制等级，因为要原子的频繁访问，所以使用atomic
        Formatter::ptr _formatter;                   // 日志格式化器
        std::vector<mylog::LogSink::ptr> _sinks;       // 落地方向数组，可以有多种落地方向
    };

    // 同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   mylog::Level::value level,
                   Formatter::ptr formatter,
                   std::vector<mylog::LogSink::ptr> sinks)
            : Logger(logger_name, level, formatter, sinks) // 父类构造
        {
        }

    protected:
        // 将日志直接通过落地模块句柄进行日志落地
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    // 同步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    mylog::Level::value level,
                    Formatter::ptr formatter,
                    std::vector<mylog::LogSink::ptr> sinks,
                    AsyncLooperType type)
            : Logger(logger_name, level, formatter, sinks), // 父类构造
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::readLog, this, std::placeholders::_1), type))
        {
        }

    protected:
        // 将数据写入缓冲区
        void log(const char *data, size_t len) override
        {
            _looper->push(data, len);
        }
        // 实际日志的落地函数——回调函数
        void readLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(buf.readBegin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper; // 异步工作器
    };

    /*使用建造者模式来建造日志器，不让用户直接去构造日志器，简化用户的使用复杂度*/
    // 1. 抽象一个日志器建造者类
    //   (1). 设置日志器类型
    //   (2). 将不同类型日志器的创建放到同一个日志器建造者类中完成
    enum LoggerType
    {
        LOGGER_SYNC, // 同步日志器
        LOGGER_ASYNC // 异步日志器
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),   // 默认是同步日志器
              _limit_level(Level::value::DEBUG),       // 默认限制等级是DEBUF
              _Async_type(AsyncLooperType::ASYNC_SAFE) // 默认是安全模式
        {
        }
        // 日志器类型
        void buildLoggerType(LoggerType type) { _logger_type = type; }
        // 异步工作器类型
        void buildAsyncLoggerType(AsyncLooperType type) { _Async_type = type; }
        // 日志器名称
        void buildLoggerName(const std::string &name) { _logger_name = name; }
        // 限制等级
        void buildLoggerLevel(Level::value level) { _limit_level = level; }
        // 日志器格式化
        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        // 落地方式
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr sink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(sink);
        }

        // 实际输出
        virtual Logger::ptr build() = 0;

    protected:
        AsyncLooperType _Async_type;      // 异步工作器类型
        LoggerType _logger_type;          // 日志器类型
        std::string _logger_name;         // 日志器名称
        Level::value _limit_level;        // 限制等级
        Formatter::ptr _formatter;        // 日志格式化
        std::vector<LogSink::ptr> _sinks; // 落地方式
    };

    // 2. 派生出具体的建造者类---局部日志器的建造者 & 全局日志器建造者
    //                                             后边添加了全局单例管理器，将日志器添加全局管理

    // 局部日志器建造者
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); // 必须有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                buildSink<StdOutSink>();
            }
            // 如果类型是异步日志器
            if (_logger_type == LoggerType::LOGGER_ASYNC)
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _Async_type);
            // 否则是同步日志器
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    // 单例日志管理器
    class LoggerManager
    {
    public:
        static LoggerManager& getInstance()
        {
            //C++11后确保了静态成员的线程安全
            //同时创建除真正创建的，其他都会阻塞
            static LoggerManager eton;
            return eton;
        }
        //添加日志器
        void addLogger(Logger::ptr &logger)
        {
            //有则退出
            if(hasLogger(logger->getName())) return;
            _loggers.insert(std::make_pair(logger->getName(),logger));
        }
        //查找日志器是否存在
        bool hasLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end()) return false;
            return true;
        }
        //返回指定日志器
        Logger::ptr getLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            //没找到返回空指针
            if(it == _loggers.end()) return Logger::ptr();

            return it->second;
        }
        //返回默认的日志器
        Logger::ptr getRootLogger()
        {
            return _root_logger;
        }
    private:    
        //构造函数私有化
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger=builder->build();
            _loggers.insert(std::make_pair("root",_root_logger));
        }
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<std::string,Logger::ptr> _loggers;//管理日志器的队列
    };

    // 全局日志器建造者
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); // 必须有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                buildSink<StdOutSink>();
            }
            Logger::ptr logger;
            // 如果类型是异步日志器
            if (_logger_type == LoggerType::LOGGER_ASYNC)
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _Async_type);
            // 否则是同步日志器
            logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            LoggerManager::getInstance().addLogger(logger);

            return Logger::ptr();
        }
    };
}